package com.yss.reportworld.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yss.common.core.constant.Constants;
import com.yss.common.core.constant.RequestDataHelper;
import com.yss.common.core.constant.SecurityConstants;
import com.yss.common.core.domain.YssResponse;
import com.yss.common.core.enums.YssConfigEnum;
import com.yss.common.core.enums.YssStatusEnum;
import com.yss.common.core.exception.BusinessException;
import com.yss.common.core.utils.DateUtils;
import com.yss.common.core.utils.StringUtils;
import com.yss.common.security.utils.DictUtils;
import com.yss.reportworld.domain.*;
import com.yss.reportworld.domain.vo.DataSqlVO;
import com.yss.reportworld.domain.vo.TableDetailVO;
import com.yss.reportworld.domain.vo.TableIndexedVO;
import com.yss.reportworld.domain.vo.TableVO;
import com.yss.reportworld.enums.ReportWorldDescriptionEnum;
import com.yss.reportworld.enums.ReportWorldEnum;
import com.yss.reportworld.enums.ReportWorldFileNameEnum;
import com.yss.reportworld.mapper.DatasqlMapper;
import com.yss.reportworld.mapper.TableDetailMapper;
import com.yss.reportworld.mapper.TableMapper;
import com.yss.reportworld.service.*;
import com.yss.reportworld.util.TableInfoUtil;
import com.yss.system.api.RemoteConfigService;
import com.yss.system.api.domain.SysDictData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Service
public class UpgradeToolsZipServiceImpl implements IUpgradeToolsZipService {

    @Autowired
    private IModularService modularService;
    @Autowired
    private IReportService reportService;
    @Autowired
    private TableMapper tableMapper;
    @Autowired
    private TableDetailMapper tableDetailMapper;
    @Autowired
    private DatasqlMapper datasqlMapper;
    @Autowired
    public IDemandBugInfoService demandBugInfoService;
    @Autowired
    private IDatabaseService databaseService;
    @Autowired
    private IDescriptionService descriptionService;
    @Autowired
    private IDataCustomerService dataCustomerService;
    @Autowired
    private IDataReportService dataReportService;
    @Autowired
    private IDataModularService dataModularService;
    @Autowired
    private IDataDemandBugService dataDemandBugService;
    @Autowired
    private ITableIndexedService tableIndexedService;
    @Autowired
    private RemoteConfigService remoteConfigService;

    public byte[] downUpgradeToolsZip(Database database) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            // 获取修改记录信息文件 取当前版本号的数据
            List<SysDictData> dictDatas = DictUtils.getDictCache(ReportWorldEnum.REPORT_VERSION.getCode());
            if (StringUtils.isEmpty(dictDatas)) {
                throw new BusinessException("没有获取到版本号，请稍后重试！");
            }
            TableInfoUtil.handleTableVersion(database.getHisVersion());
            Map<String, String> mapFile = new HashMap<>();
            // 模块信息
            String modular = modularService.getModularJsonInfo();
            mapFile.put(ReportWorldFileNameEnum.MODULAR_JSON.getCode() + "." + ReportWorldFileNameEnum.MODULAR_JSON.getDesc(), modular);
            // 报表信息
            String report = reportService.getReportJsonInfo();
            mapFile.put(ReportWorldFileNameEnum.REPORT_JSON.getCode() + "." + ReportWorldFileNameEnum.REPORT_JSON.getDesc(), report);
            // 需求BUG文件
            String demandBugInfo = demandBugInfoService.getDemandBugJsonInfo();
            mapFile.put(ReportWorldFileNameEnum.DEMAND_BUG.getCode() + "." + ReportWorldFileNameEnum.DEMAND_BUG.getDesc(), demandBugInfo);
            //生成版本信息文件
            mapFile.put(ReportWorldFileNameEnum.VERSION.getCode() + "." + ReportWorldFileNameEnum.VERSION.getDesc(),
                    (StringUtils.isNotEmpty(database.getHisVersion()) ? database.getHisVersion() : dictDatas.get(0).getDictValue()) + ":" + DateUtils.dateToStr(new Date()));
            // 报表世界升级工具数据对比策略信息
            QueryWrapper<Description> descriptionQueryWrapper = new QueryWrapper<>();
            descriptionQueryWrapper.eq("NAME", ReportWorldDescriptionEnum.COMPARECONFIG.getCode());
            Description description = descriptionService.getOne(descriptionQueryWrapper);
            if (null == description || StringUtils.isBlank(description.getDescription())) {
                throw new BusinessException("获取升级工具对比数据策略信息失败，请稍后重试！");
            }
            mapFile.put(ReportWorldFileNameEnum.COMPARECONFIG.getCode() + "." + ReportWorldFileNameEnum.COMPARECONFIG.getDesc(), description.getDescription());
            // 获取导出ZIP所需要的表结构，基础数据，SQL数据
            this.queryZipByData(mapFile, database.getHisVersion());
            if (StringUtils.isEmpty(mapFile)) {
                throw new BusinessException("没有获取到要生成文件的信息，请稍后重试！");
            }
            ZipOutputStream zip = new ZipOutputStream(outputStream);
            YssResponse<String> response = remoteConfigService.getConfigByKey(YssConfigEnum.REPORTWORLD_ZIP_RESOURCES.getCode(), SecurityConstants.INNER);
            String path = StringUtils.isEmpty(response.getData()) ? "" : response.getData();
            for (Map.Entry<String, String> entry : mapFile.entrySet()) {
                // 添加到zip
                zip.putNextEntry(new ZipEntry(path + entry.getKey()));
                IOUtils.write(entry.getValue(), zip, Constants.UTF8);
                zip.flush();
                zip.closeEntry();
            }
            IOUtils.closeQuietly(zip);
        } catch (Exception ex) {
            log.error("下载ZIP文件失败", ex.getMessage());
            throw new BusinessException("下载ZIP文件失败" + ex.getMessage());
        } finally {
            RequestDataHelper.removeThreadLocal();
        }
        return outputStream.toByteArray();
    }

    /**
     * 获取导出ZIP所需要的表结构，基础数据，SQL数据
     *
     * @param mapFile
     */
    private void queryZipByData(Map<String, String> mapFile, String downversion) {
        if (StringUtils.isEmpty(mapFile)) {
            log.error("没有获取到存放数据的集合信息！");
            return;
        }
        long startTime = System.currentTimeMillis();
        // 获取客户关联信息
        Map<String, List<String>> mapDataCustomer = dataCustomerService.getDataCustomerMap();
        // 获取报表关联信息
        Map<String, List<String>> mapDataReport = dataReportService.getDataReportMap();
        // 获取模块关联信息
        Map<String, List<String>> mapDataModular = dataModularService.getDataModularMap();
        // 获取需求BUG关联信息
        Map<String, List<String>> mapDataDemandBug = dataDemandBugService.getDataDemandBugMap();
        this.getDataBaseInfoJson(mapDataCustomer, mapDataReport, mapDataModular, mapDataDemandBug, mapFile, downversion); //处理基础数据
        this.getDataSqlInfoJson(mapDataCustomer, mapDataReport, mapDataModular, mapDataDemandBug, mapFile); // 处理SQL脚本数据
        this.getTableInfoJson(mapDataCustomer, mapDataReport, mapDataModular, mapDataDemandBug, mapFile);// 处理表数据
        //任务执行总时长
        long times = System.currentTimeMillis() - startTime;
        log.info("  总共耗时：" + times / 1000 + "秒");
    }


    /**
     * 处理基础数据
     *
     * @param mapDataCustomer
     * @param mapDataReport
     * @param mapDataModular
     * @param mapDataDemandBug
     * @param mapFile
     */
    private void getDataBaseInfoJson(Map<String, List<String>> mapDataCustomer,
                                     Map<String, List<String>> mapDataReport,
                                     Map<String, List<String>> mapDataModular,
                                     Map<String, List<String>> mapDataDemandBug,
                                     Map<String, String> mapFile, String downversion) {
        // 基础数据
        Map<String, List<Object>> objectMap = databaseService.queryAllDataList(downversion);
        if (StringUtils.isEmpty(objectMap)) {
            log.error("没有获取到要导出的基础数据信息！");
            return;
        }
        // 字典信息是否包含客户信息
        YssResponse<String> responsecustomer = remoteConfigService.getConfigByKey(YssConfigEnum.DATABASE_INFO_JSON_DATADICT_CUSTOMER.getCode(), SecurityConstants.INNER);
        // 字典信息是否包含报表信息
        YssResponse<String> responsereport = remoteConfigService.getConfigByKey(YssConfigEnum.DATABASE_INFO_JSON_DATADICT_REPORT.getCode(), SecurityConstants.INNER);
        // 字典信息是否包含模块信息
        YssResponse<String> responsemodular = remoteConfigService.getConfigByKey(YssConfigEnum.DATABASE_INFO_JSON_DATADICT_MODULAR.getCode(), SecurityConstants.INNER);
        Map<String, String> josnMap = new HashMap();
        for (Map.Entry<String, List<Object>> entry : objectMap.entrySet()) { // 所有基础数据
            List<Object> list = entry.getValue();
            if (StringUtils.isBlank(entry.getKey()) || StringUtils.isEmpty(list)) {
                continue;
            }
            for (Object object : list) { // 一个类型的基础数据
                if (null == object) {
                    continue;
                }
                Map<String, Object> columMap = (Map<String, Object>) object; // 每一条数据
                if (StringUtils.isEmpty(columMap)) {
                    continue;
                }
                Object primaryKeyValue = columMap.get("RW_DB_ID"); //主键的值
                if (null == primaryKeyValue || StringUtils.isBlank(primaryKeyValue.toString())) {
                    continue;
                }
                if (!StringUtils.isEmpty(mapDataModular)) {
                    columMap.put("listModular", mapDataModular.get(entry.getKey() + "\t" + primaryKeyValue));
                }
                if (!StringUtils.isEmpty(mapDataReport)) {
                    columMap.put("listReport", mapDataReport.get(entry.getKey() + "\t" + primaryKeyValue));
                }
                if (!StringUtils.isEmpty(mapDataCustomer)) {
                    columMap.put("listCustomer", mapDataCustomer.get(entry.getKey() + "\t" + primaryKeyValue));
                }
                if (!StringUtils.isEmpty(mapDataDemandBug)) {
                    columMap.put("listDemandBug", mapDataDemandBug.get(entry.getKey() + "\t" + primaryKeyValue));
                }
                if (StringUtils.oneCase(entry.getKey(), "RW_DB_TP_GL_EXHIB_DATADICT")) {
                    if (!"true".equals(responsemodular.getData())) {
                        columMap.put("listModular", null);
                    }
                    if (!"true".equals(responsereport.getData())) {
                        columMap.put("listReport", null);
                    }
                    if (!"true".equals(responsecustomer.getData())) {
                        columMap.put("listCustomer", null);
                    }
                }
                // 每次导出的时候删除主键信息，主键是系统的
                columMap.remove("RW_DB_ID");
            }
            String json = JSONObject.toJSONString(list, SerializerFeature.WriteMapNullValue);
            josnMap.put(entry.getKey().toUpperCase().replace(ReportWorldEnum.RW_DB_SUFFIX.getCode(), ""), json);
        }
        String baseData = JSONObject.toJSONString(josnMap, SerializerFeature.WriteMapNullValue);
        mapFile.put(ReportWorldFileNameEnum.DATABASE_JSON.getCode() + "." + ReportWorldFileNameEnum.DATABASE_JSON.getDesc(), baseData);
    }

    /**
     * 处理SQL数据
     *
     * @param mapDataCustomer
     * @param mapDataReport
     * @param mapDataModular
     * @param mapDataDemandBug
     * @param mapFile
     */
    private void getDataSqlInfoJson(Map<String, List<String>> mapDataCustomer,
                                    Map<String, List<String>> mapDataReport,
                                    Map<String, List<String>> mapDataModular,
                                    Map<String, List<String>> mapDataDemandBug,
                                    Map<String, String> mapFile) {
        // sql信息
        QueryWrapper<Datasql> dataSqlQueryWrapper = new QueryWrapper<>();
        dataSqlQueryWrapper.in("STATUS", YssStatusEnum.getUpgradeToolsZipStatus());
        List<Datasql> dataSqlList = datasqlMapper.selectList(dataSqlQueryWrapper);
        if (StringUtils.isEmpty(dataSqlList)) {
            log.error("没有获取到SQL脚本相关信息！");
            return;
        }
        List<DataSqlVO> listvo = new ArrayList<>();
        for (Datasql dataSql : dataSqlList) {
            if (null == dataSql || StringUtils.isBlank(dataSql.getId())) {
                continue;
            }
            DataSqlVO vo = new DataSqlVO();
            BeanUtils.copyProperties(dataSql, vo);
            if (!StringUtils.isEmpty(mapDataModular)) {
                vo.setListModular(mapDataModular.get(ReportWorldEnum.REPORT_DATASQL_CODE.getCode() + "\t" + dataSql.getId()));
            }
            if (!StringUtils.isEmpty(mapDataReport)) {
                vo.setListReport(mapDataReport.get(ReportWorldEnum.REPORT_DATASQL_CODE.getCode() + "\t" + dataSql.getId()));
            }
            if (!StringUtils.isEmpty(mapDataCustomer)) {
                vo.setListCustomer(mapDataCustomer.get(ReportWorldEnum.REPORT_DATASQL_CODE.getCode() + "\t" + dataSql.getId()));
            }
            if (!StringUtils.isEmpty(mapDataDemandBug)) {
                vo.setListDemandBug(mapDataDemandBug.get(ReportWorldEnum.REPORT_DATASQL_CODE.getCode() + "\t" + dataSql.getId()));
            }
            listvo.add(vo);
        }
        String json = JSONObject.toJSONString(listvo, SerializerFeature.WriteMapNullValue);
        mapFile.put(ReportWorldFileNameEnum.SQL_JSON.getCode() + "." + ReportWorldFileNameEnum.SQL_JSON.getDesc(), json);
    }


    /**
     * 处理表数据信息，包含表的索引，和字段信息
     *
     * @param mapDataCustomer
     * @param mapDataReport
     * @param mapDataModular
     * @param mapDataDemandBug
     * @param mapFile
     */
    private void getTableInfoJson(Map<String, List<String>> mapDataCustomer,
                                  Map<String, List<String>> mapDataReport,
                                  Map<String, List<String>> mapDataModular,
                                  Map<String, List<String>> mapDataDemandBug,
                                  Map<String, String> mapFile) {
        // 表结构信息
        QueryWrapper<Table> tableQueryWrapper = new QueryWrapper<>();
        tableQueryWrapper.eq("FTYPE", 1); //表类型，数据库表
        tableQueryWrapper.eq("FSOURCE", 1); //表来源，报表世界
        tableQueryWrapper.in("STATUS", YssStatusEnum.getUpgradeToolsZipStatus());
        List<Table> tableList = tableMapper.selectList(tableQueryWrapper);
        Map<String, List<TableDetailVO>> mapTableDetail = this.queryTableDetailVoMap(mapDataCustomer, mapDataReport, mapDataModular, mapDataDemandBug); // 获取表字段信息
        if (StringUtils.isEmpty(mapTableDetail)) {
            throw new BusinessException("没有获取到表字段信息，请检查表结构信息！");
        }
        Map<String, List<TableIndexedVO>> mapTableIndexedVo = this.queryTableIndexedVoMap();// 获取表索引信息
        List<TableVO> listVo = new ArrayList<>();
        for (Table table : tableList) {
            if (null == table || StringUtils.isBlank(table.getId())) {
                continue;
            }
            TableVO vo = new TableVO();
            BeanUtils.copyProperties(table, vo);
            // 表字段信息 没有字段信息的表跳过
            if (!mapTableDetail.containsKey(table.getId()) || StringUtils.isEmpty(mapTableDetail.get(table.getId()))) {
                log.error(table.getName() + "没有获取到字段信息！");
                throw new BusinessException(table.getName() + "没有获取到字段信息，请检查表字段信息！");
            }
            vo.setListTableDetail(mapTableDetail.get(table.getId()));
            if (!StringUtils.isEmpty(mapDataModular)) {
                vo.setListModular(mapDataModular.get(ReportWorldEnum.REPORT_TABLE_CODE.getCode() + "\t" + table.getId()));
            }
            if (!StringUtils.isEmpty(mapDataReport)) {
                vo.setListReport(mapDataReport.get(ReportWorldEnum.REPORT_TABLE_CODE.getCode() + "\t" + table.getId()));
            }
            if (!StringUtils.isEmpty(mapDataCustomer)) {
                vo.setListCustomer(mapDataCustomer.get(ReportWorldEnum.REPORT_TABLE_CODE.getCode() + "\t" + table.getId()));
            }
            if (!StringUtils.isEmpty(mapDataDemandBug)) {
                vo.setListDemandBug(mapDataDemandBug.get(ReportWorldEnum.REPORT_TABLE_CODE.getCode() + "\t" + table.getId()));
            }
            if (!StringUtils.isEmpty(mapTableIndexedVo)) {
                vo.setListIndexed(mapTableIndexedVo.get(table.getId()));
            }
            listVo.add(vo);
        }
        String json = JSONObject.toJSONString(listVo, SerializerFeature.WriteMapNullValue);
        mapFile.put(ReportWorldFileNameEnum.REPORT_TABLE_FILENAME_JSON.getCode() + "."
                + ReportWorldFileNameEnum.REPORT_TABLE_FILENAME_JSON.getDesc(), json);
    }

    /**
     * 获取表字段信息
     *
     * @param mapDataCustomer
     * @param mapDataReport
     * @param mapDataModular
     * @param mapDataDemandBug
     */
    private Map<String, List<TableDetailVO>> queryTableDetailVoMap(Map<String, List<String>> mapDataCustomer,
                                                                   Map<String, List<String>> mapDataReport,
                                                                   Map<String, List<String>> mapDataModular,
                                                                   Map<String, List<String>> mapDataDemandBug) {
        // 表字段信息
        QueryWrapper<TableDetail> tableDetailQueryWrapper = new QueryWrapper<>();
        tableDetailQueryWrapper.in("STATUS", YssStatusEnum.getUpgradeToolsZipStatus());
        tableDetailQueryWrapper.orderByAsc("ORDER_NUM");
        List<TableDetail> tableDetailList = tableDetailMapper.selectList(tableDetailQueryWrapper);
        // 表字段信息是否包含客户信息
        YssResponse<String> responsecustomer = remoteConfigService.getConfigByKey(YssConfigEnum.REPORTWORLD_TABLE_DETAIL_CUSTOMER.getCode(), SecurityConstants.INNER);
        // 表字段信息是否包含报表信息
        YssResponse<String> responsereport = remoteConfigService.getConfigByKey(YssConfigEnum.REPORTWORLD_TABLE_DETAIL_REPORT.getCode(), SecurityConstants.INNER);
        // 表字段信息是否包含模块信息
        YssResponse<String> responsemodular = remoteConfigService.getConfigByKey(YssConfigEnum.REPORTWORLD_TABLE_DETAIL_MODULAR.getCode(), SecurityConstants.INNER);
        if (StringUtils.isEmpty(tableDetailList)) {
            log.error("没有获取到表字段信息！");
            return null;
        }
        // 表字段处理
        Map<String, List<TableDetailVO>> mapTableDetail = new HashMap();
        for (TableDetail detail : tableDetailList) {
            if (null == detail || StringUtils.isBlank(detail.getTableId())) {
                continue;
            }
            TableDetailVO detailVO = new TableDetailVO();
            BeanUtils.copyProperties(detail, detailVO);
            if (!StringUtils.isEmpty(mapDataModular) && "true".equals(responsemodular.getData())) {
                detailVO.setListModular(mapDataModular.get(ReportWorldEnum.REPORT_TABLE_DETAIL_CODE.getCode() + "\t" + detail.getId()));
            }
            if (!StringUtils.isEmpty(mapDataReport) && "true".equals(responsereport.getData())) {
                detailVO.setListReport(mapDataReport.get(ReportWorldEnum.REPORT_TABLE_DETAIL_CODE.getCode() + "\t" + detail.getId()));
            }
            if (!StringUtils.isEmpty(mapDataCustomer) && "true".equals(responsecustomer.getData())) {
                detailVO.setListCustomer(mapDataCustomer.get(ReportWorldEnum.REPORT_TABLE_DETAIL_CODE.getCode() + "\t" + detail.getId()));
            }
            if (!StringUtils.isEmpty(mapDataDemandBug)) {
                detailVO.setListDemandBug(mapDataDemandBug.get(ReportWorldEnum.REPORT_TABLE_DETAIL_CODE.getCode() + "\t" + detail.getId()));
            }
            if (mapTableDetail.containsKey(detailVO.getTableId())) {
                List<TableDetailVO> list = mapTableDetail.get(detailVO.getTableId());
                list.add(detailVO);
                mapTableDetail.put(detail.getTableId(), list);
            } else {
                List<TableDetailVO> list = new ArrayList<>();
                list.add(detailVO);
                mapTableDetail.put(detailVO.getTableId(), list);
            }
        }
        return mapTableDetail;
    }

    /**
     * 获取表的索引信息
     *
     * @return
     */
    private Map<String, List<TableIndexedVO>> queryTableIndexedVoMap() {
        // 索引信息
        Map<String, List<TableIndexed>> mapTableIndexed = tableIndexedService.getTableIndexedMapByZip();
        if (StringUtils.isEmpty(mapTableIndexed)) {
            return null;
        }
        // 索引处理成JSON格式需要的
        Map<String, List<TableIndexedVO>> mapTableIndexedVo = new HashMap();
        for (Map.Entry<String, List<TableIndexed>> entry : mapTableIndexed.entrySet()) {
            List<TableIndexed> list = entry.getValue();
            Map<String, TableIndexedVO> mapIndexVo = new LinkedHashMap<>();
            for (TableIndexed indexed : list) {
                if (mapIndexVo.containsKey(indexed.getIndexedName())) { // 存在索引名称的
                    TableIndexedVO vo = mapIndexVo.get(indexed.getIndexedName());
                    String columnName = indexed.getIndexedColumnName()
                            + (StringUtils.isNotBlank(indexed.getIndexedAscDesc()) ? " " + indexed.getIndexedAscDesc() : "");
                    vo.setIndexColumnName(vo.getIndexColumnName() + "," + columnName);
                    mapIndexVo.put(indexed.getIndexedName(), vo);
                } else {
                    TableIndexedVO vo = new TableIndexedVO();
                    String columnName = indexed.getIndexedColumnName()
                            + (StringUtils.isNotBlank(indexed.getIndexedAscDesc()) ? " " + indexed.getIndexedAscDesc() : "");
                    vo.setIndexColumnName(columnName);
                    vo.setIndexType(indexed.getIndexedType());
                    vo.setStatus(indexed.getStatus());
                    vo.setIndexName(indexed.getIndexedName());
                    mapIndexVo.put(indexed.getIndexedName(), vo);
                }
            }
            List<TableIndexedVO> listvo = new ArrayList<>();
            for (Map.Entry<String, TableIndexedVO> entryVo : mapIndexVo.entrySet()) {
                if (null == entryVo.getValue()) {
                    continue;
                }
                listvo.add(entryVo.getValue());
            }
            mapTableIndexedVo.put(entry.getKey(), listvo);
        }
        return mapTableIndexedVo;
    }

}
